const _ = require('lodash');
const async = require('async');
const mongoose = require('mongoose');
const BaseModel = {

	_sanitize: function(query){
		let ret = _.pickBy(query, (value) => {
			if(value !== undefined && value !== null && _.trim(value) !== ''){
				return true;
			}
		});
		return ret;
	},

	_transId: function(query){
		Object.keys(query).map((key) => {
			if(/^[0-9a-z]{24}$/.test(query[key])){
				query[key] = mongoose.Types.ObjectId(query[key]);
			}
		});
	},	

	/**
	 * 基于aggregate的查询，通常用于返回具有分页信息的数据查询结果集。
	 * @param {[Request]} req 查询对象。具有分页和查询参数，这样的查询只应该直接来自于客户端请求，req的查询参数
	 * req.query = {
	 * -----------查询参数，对应于$match指定的查询对象--------------
	 * somefield1: 'xxx',
	 * somefield2: 'xxx',
	 * somefield3: 'xxx',
	 * -----------分页与排序参数[可选]--------------
	 * 	'pageSize' : 1,
	 * 	'currentPage': 1,
	 * 	'sortField': 'someField',
	 * 	'sortOrder': 'descend|asc',
	 * }
	 * @param  {[Array]}   aggregateStages [对应于aggregate的pipeline中除了$match以外的各个环节]
	 * @param  {Function} callback        [description]
	 * @return {[Object]}                   [description]
	 * ret = {
					results : docs,
					info: {
						pageSize: 每页数据条数,
						current: 当前页数,
						total: 满足当前查询条件的总数据条数
					}
				};
	 * 如果req.query中没有检测到分页相关的参数，那么最终数据集会返回以下格式，保持返回格式统一
	 * ret = {
			results : docs,
			info: {
				pageSize: N,
				current: 1,
				total: N
			}
		};
		如果希望将所有数据一次性返回不进行分页，需要明确传递pageSize为'all'，如果不传递任何分页数据，那么会使用
		默认pageSize作为结果集的上限，而不是返回所有数据。如希望返回任意结果集而不受分页限制，可以直接使用BaseDao:query
		方法。
	 *       
	 */

	getList : function(req, aggregateStages, callback){

		if(typeof aggregateStages === 'function'){
			callback = aggregateStages;
			aggregateStages = [];
		}

		req.query = this._sanitize(req.query);
		this._transId(req.query);
		
		// 如果有通过外键属性进行查询的情况(例如，在产品表中通过分类名称进行过滤)，那么需要在Request.query对象中加入__queryStage属性来指定
		/**
		 * [Request.query.__queryStage]
		 * Request.query.__queryStage = {
		 * 		用于表示当前aggregateStages中，从下标0到下标index中的stage是用来进行过滤的
		 * 		index: 2, 
		 * 		指定当前request.query对象中哪些属性是通过外键关联的doc的属性
		 * 		deleteKeys: ['name']
		 * }
		 * @type {Object}
		 */
		let queryStage = [];
		if(req.query.__queryStage){
			let index = req.query.__queryStage.index;
			let toDeleteKeys = req.query.__queryStage.deleteKeys || [];
			queryStage = aggregateStages.slice(0, index + 1);
			// 删除在查询对象中给定的外键对象中属性
			toDeleteKeys.push('__queryStage');
			_.each(toDeleteKeys, (key) => {
				_.unset(req.query, key);
			})
		}

		const optionKeys = ['pageSize', 'currentPage', 'sortField', 'sortOrder'];
		let optionsData = _.pick(req.query, optionKeys);
		let condition = _.omit(req.query, optionKeys);
		
		async.waterfall([
			(callback) => {

				this.dao.count([{'$match': condition}, ...queryStage], (err, total) => {
					callback(err, total);
				});
			},
			(total, callback) => {
			
				optionsData.pageSize = optionsData.pageSize === 'all' ? 'all' : parseInt(optionsData.pageSize || 6);
				optionsData.currentPage = parseInt(optionsData.currentPage - 0 || 1);

				if(optionsData.pageSize !== 'all'){
					let realPageCount = Math.ceil(total / optionsData.pageSize) === 0 ? 1 : Math.ceil(total / optionsData.pageSize);
					if( optionsData.currentPage > realPageCount ){
						optionsData.currentPage = realPageCount;
					}
				}

				let options = optionsData.pageSize !== 'all' ? {
						skip: (optionsData.currentPage - 1) * optionsData.pageSize,
						limit: parseInt(optionsData.pageSize),
					}: {};

				if(optionsData.sortField){
					options.sort = { [optionsData.sortField] : optionsData.sortOrder === 'descend' ? -1 : 1 };
				}

				this.dao.query([{'$match': condition}].concat(aggregateStages), options, (err,docs) => {
					callback(err, docs, total);
				});
			},
		],(err, docs, total) => {
			if(!err){
				let ret = {
					results : docs,
					info: {
						pageSize: optionsData.pageSize !== 'all' ? optionsData.pageSize : total,
						current: optionsData.currentPage,
						total
					}
				};
				callback(err, ret);
				return;
			}
			callback(err);
		});

	},

};

module.exports = BaseModel;